/*            Copyright(c) 2006-2008 Dave Sexton           *
 * http://www.codeplex.com/DocProject                      *
 * http://www.codeplex.com/DocProject/Project/License.aspx *
 ***********************************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;

namespace DaveSexton.DocProject
{
	public class CollectionConverter<T> : TypeConverter
	{
		#region Public Properties
		public bool RemoveEmptyEntries
		{
			get
			{
				return removeEmptyEntries;
			}
		}

		public ICollection<char> Separators
		{
			get
			{
				return new ReadOnlyCollection<char>(new List<char>(separators));
			}
		}
		#endregion

		#region Private / Protected
		private readonly TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
		private readonly char[] separators;
		private bool removeEmptyEntries;
		#endregion

		#region Constructors
		/// <summary>
		/// Constructs a new instance of the <see cref="CollectionConverter" /> class.
		/// </summary>
		public CollectionConverter()
		{
			separators = new char[] { ',' };
			removeEmptyEntries = true;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="CollectionConverter" /> class.
		/// </summary>
		public CollectionConverter(bool removeEmptyEntries)
		{
			separators = new char[] { ',' };
			this.removeEmptyEntries = removeEmptyEntries;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="CollectionConverter" /> class.
		/// </summary>
		public CollectionConverter(bool removeEmptyEntries, params char[] separators)
		{
			if (separators == null)
				throw new ArgumentNullException("separators");

			this.removeEmptyEntries = removeEmptyEntries;
			this.separators = separators;
		}

		/// <summary>
		/// Constructs a new instance of the <see cref="CollectionConverter" /> class.
		/// </summary>
		public CollectionConverter(params char[] separators)
		{
			if (separators == null)
				throw new ArgumentNullException("separators");

			this.separators = separators;
			removeEmptyEntries = true;
		}
		#endregion

		#region Methods
		public virtual ICollection<T> CreateCollection()
		{
			return new Collection<T>();
		}

		public override bool CanConvertTo(System.ComponentModel.ITypeDescriptorContext context, Type destinationType)
		{
			if (destinationType == typeof(string))
				return true;
			else
				return base.CanConvertTo(context, destinationType);
		}

		public override bool CanConvertFrom(System.ComponentModel.ITypeDescriptorContext context, Type sourceType)
		{
			if (sourceType == typeof(string))
				return true;
			else
				return base.CanConvertFrom(context, sourceType);
		}

		public override object ConvertFrom(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
		{
			string s = value as string;

			if (s == null)
				return base.ConvertFrom(context, culture, value);
			else
			{
				if (converter == null || !converter.CanConvertFrom(context, typeof(string)))
					throw new InvalidOperationException(string.Format(System.Globalization.CultureInfo.CurrentCulture, 
						Resources.Errors.NoTypeConverterForConvertFromString, typeof(T).FullName));

				ICollection<T> collection = CreateCollection();

				foreach (string item in s.Split(separators,
					(removeEmptyEntries) ? StringSplitOptions.RemoveEmptyEntries : StringSplitOptions.None))
				{
					collection.Add((T) converter.ConvertFromString(context, culture, item));
				}

				return collection;
			}
		}

		public override object ConvertTo(System.ComponentModel.ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
		{
			IEnumerable<T> collection = value as IEnumerable<T>;

			if (collection != null && destinationType == typeof(string))
			{
				StringBuilder result = new StringBuilder();
				StringBuilder separator = new StringBuilder(separators.Length);
				separator.Append(separators);

				bool useConverter = converter != null && converter.CanConvertTo(context, typeof(string));

				foreach (T item in collection)
				{
					if (useConverter)
						result.AppendFormat("{0}{1}", converter.ConvertToString(context, culture, item), separator);
					else
						result.AppendFormat("{0}{1}", item, separator);
				}

				if (result.Length > 0)
				// remove trailing separator
				{
					int length = separator.Length;
					result.Remove(result.Length - length, length);
				}

				return result.ToString();
			}
			else
				return base.ConvertTo(context, culture, value, destinationType);
		}
		#endregion
	}
}
